package gf

import (
	"fmt"
	"github.com/gabriel-vasile/mimetype"
	"os"
	"path/filepath"
)

// GetFileFormat 获取文件的格式,通过读取文件内容实现,而不是文件名后缀
// 参数:
//
//	filename: 文件名
//
// 返回值:
//
//	string: 文件格式的扩展名
//	error: 错误信息，如果执行成功则为nil
func GetFileFormat(filename string) (string, error) {
	// 判断文件是否存在
	_, err := os.Stat(filename)
	if err != nil {
		return "", err
	}

	// 打开文件
	file, err := os.Open(filename)
	defer func(file *os.File) {
		err := file.Close()
		if err != nil {
			logs.Error(err.Error())
		}
	}(file)
	if err != nil {
		logs.Error(err.Error())
		return "", err
	}

	// 读取文件的前512字节以确定文件类型
	buf := make([]byte, 512)
	_, err = file.Read(buf)
	if err != nil {
		logs.Error(err.Error())
		return "", err
	}

	// 使用读取的内容检测文件的MIME类型
	mt := mimetype.Detect(buf)
	if err != nil {
		logs.Error(err.Error())
		return "", err
	}

	// 返回文件格式的扩展名
	return mt.Extension(), nil
}

// GetFileFormatName 根据文件名获取文件格式名
// 参数:
//
//	filename string: 文件名
//
// 返回值:
//
//	string: 文件的格式名，如".txt"、".jpg"等,不支持多段后缀,例如: .tar.gz
func GetFileFormatName(filename string) string {
	// 使用filepath.Ext函数提取文件名中的扩展名
	ext := filepath.Ext(filename)
	// 返回文件的格式名
	return ext
}

// GetSubFIleFormat 获取指定目录下具有特定格式的文件列表。
//
// 参数:
//
//   - dir: 目录路径，例如 "D:/code/gitee/go/public"
//
//   - formatName: 文件格式名称，例如 ".txt"
//
// 返回值:
//
//   - []string: 符合指定格式的文件路径列表，例如 ["D:/code/gitee/go/public/file1.txt", "D:/code/gitee/go/public/file2.txt"]
//
//   - error: 如果发生错误，返回相应的错误对象；否则返回 nil
func GetSubFIleFormat(dir, formatName string) ([]string, error) {
	// 初始化一个空的字符串切片，用于存储符合指定格式的文件路径。
	var fl []string

	// 调用 PathGetSubFIle 方法获取指定目录下的所有文件列表。
	fileList, err := GetSubFIle(dir)
	if err != nil {
		return nil, err
	}

	// 检查文件列表是否为空，如果为空，则设置错误信息并返回。
	if len(fileList) == 0 {
		err = fmt.Errorf("目标路径下找不到文件: %s", dir)
		return nil, err
	}

	// 遍历文件列表，筛选出具有指定格式的文件。
	for _, file := range fileList {
		if GetFileFormatName(file) == formatName {
			fl = append(fl, file)
		}
	}

	// 更新文件列表，只保留符合指定格式的文件路径。
	return fl, nil
}

func getFileInfo(filename string) (int, string) {
	// 获取文件大小
	fi, err := os.Stat(filename)
	if err != nil {
		// 如果发生错误（如文件不存在或权限问题），返回默认值
		return 0, ""
	}
	// 返回文件大小和修改时间
	return int(fi.Size()), fi.ModTime().Format("2006-01-02 15:04:05")
}

// GetFileSize 返回给定文件名的文件大小。
// 该函数首先调用 getFileInfo 获取文件的原始大小，然后将这个大小转换为更易于人类阅读的格式。
// 参数:
//
//	filename - 文件名字符串。
//
// 返回值:
//
//	文件的原始大小（以字节为单位）。
//	文件大小的易读表示形式，例如 "1.5MB"。
func GetFileSize(filename string) (int, string) {
	// 从 getFileInfo 函数获取文件大小，忽略错误处理，因为此处认为 getFileInfo 总是能成功执行。
	size, _ := getFileInfo(filename)

	// 计算文件大小为人类阅读的格式，如：1.5MB
	return size, GetHumanReadableSize(size)
}

// GetHumanReadableSize 将文件大小转换为人类可读的格式。
// 参数 size 是文件的大小，以字节为单位。
// 返回值是一个字符串，表示转换后的大小，带有适当的单位（B, KB, MB, GB, TB）。
func GetHumanReadableSize(size int) string {
	// 如果文件大小为0或负数，则直接返回"大小为0"。
	if size <= 0 {
		return "大小为0"
	}

	// 定义单位数组，从字节到太字节。
	units := []string{"B", "KB", "MB", "GB", "TB"}
	// 初始化单位和值变量。
	var unit string
	var value float64

	// 遍历单位数组，直到找到合适的单位。
	for _, u := range units {
		// 如果大小小于1024，则找到了合适的单位。
		if size < 1024 {
			unit = u
			break
		}
		// 否则，继续除以1024以找到更合适的单位。
		size /= 1024
		value = float64(size)
	}

	// 使用格式化字符串返回大小和单位。
	return fmt.Sprintf("%.2f%s", value, unit)
}

// GetFileModTime 返回指定文件的修改时间。
// 该函数仅关注文件的修改时间，忽略文件大小等其他信息。
// 参数:
//
//	filename: 需要获取修改时间的文件的路径。
//
// 返回值:
//
//	修改时间的字符串表示。
func GetFileModTime(filename string) string {
	// 调用 GetFileSize 函数，获取文件的修改时间。
	// 注意：此处忽略了文件大小的返回值，仅使用修改时间。
	_, modTime := getFileInfo(filename)
	return modTime
}

// GetFileAbsPath 获取文件的绝对路径
// 参数:
//
//	p: 文件的相对路径或绝对路径
//
// 返回值:
//
//	string: 文件的绝对路径，如果获取失败则返回空字符串
//
// 此函数尝试将给定的文件路径转换为绝对路径，以便在文件系统中精确定位文件位置
// 如果转换过程中遇到错误，将记录错误信息并返回空字符串
func GetFileAbsPath(p string) string {
	if !Exists(p) {
		return ""
	}
	absPath, err := filepath.Abs(p)
	if err != nil {
		// 当无法获取文件绝对路径时，记录错误信息
		logs.Error("无法获取文件绝对路径: ", p, " - ", err)
		return ""
	}
	return absPath
}

// GetFileBaseName 返回文件的基名。
// 该函数接受一个文件路径字符串作为参数，并返回路径中的最后一个文件名或目录名。
// 如果输入的文件路径为空或不包含有效的文件名，则返回空字符串。
//
// 参数:
//
//	filename (string): 文件路径字符串,例如: text/dir/1.zip
//
// 返回值:
//
//	string: 文件的基名: 1.zip
func GetFileBaseName(filename string) string {
	if !Exists(filename) {
		return ""
	}
	return filepath.Base(filename)
}
